﻿//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Core.h".
//-------------------------------------------------------------------

#ifndef _UXS_UNI_ARRAY_TYPE_H
#define _UXS_UNI_ARRAY_TYPE_H

#pragma once

#include "CopyStrategy.h"
#include "../CompileTime/TTypeInfo.h"

namespace UX_STL
{
namespace UXTypes
{


	//
	// Universal Array
	//

#	define TUniArray		uni_array

	template <typename T>
	struct TUniArray
	{
	// types
	public:
		typedef TMemoryViewer<T>			vis_mem_t;
		typedef T							value_t;
		typedef TUniArray<T>				Self;
		typedef TUniArray<const T>			Self_const;

		typedef typename TypeTraits::CopyConstToPointer< T, void *>::type		void_ptr_t;

	private:
		struct _CompareElements
		{
			bool operator () (const T& left, const T& right) const
			{
				return left == right;
			}
		};
		

	// variables
	private:
		union {
			T	*		_pElements;
			vis_mem_t	_pVisible;
		};
		usize			_uCount;


	// methods
	public:
		TUniArray ();
		TUniArray (T *pElements, usize uCount);
		TUniArray (void_ptr_t pBegin, void_ptr_t pEnd);

		//template <typename B>
		//TUniArray(const B *p);

		template <usize I>
		TUniArray (const T (&arr)[I]);

		T		*	ptr ();
		T const	*	ptr () const;

		T const *	raw_ptr () const			{ return _pElements; }
		
		T		&	Back ();
		T const	&	Back () const;
		T		&	Front ();
		T const	&	Front () const;

		T		&	operator [] (usize i);
		T const	&	operator [] (usize i) const;
		
		bool		operator == (const Self &sArray) const;
		bool		operator != (const Self &sArray) const;

		bool		operator ! ()	const		{ return not Empty(); }

		operator	Self_const ()	const		{ return Self_const( _pElements, _uCount ); }

		bool		Empty ()		const		{ return _uCount == 0; }		// пустой?
		usize		Count ()		const		{ return _uCount; }				// количество элементов
		usize		Size ()			const		{ return _uCount*sizeof(T); }	// размер видимой памяти
		usize		LastIndex ()	const		{ return Count()-1; }			// индекс последнего элемента

		template <typename Cmp>
		bool		Compare (const Self &sArray, Cmp sCmp) const;
		
		usize		GetIndex (const T &elem) const;
		bool		IsInArray (const T &elem) const;
		
		template <typename E>
		bool		Find (OUT usize &uIndex, const E &tElem, usize uStart = 0) const;	// найти элемент

		template <typename E>
		bool		IsExist (const E &tElem) const;


		Self		SubArray (usize first)						{ return SubArray( first, Count() - first ); }
		Self		SubArray (usize first, usize count);

		Self_const	SubArray (usize first) const				{ return SubArray( first, Count() - first ); }
		Self_const	SubArray (usize first, usize count) const;

		typedef	T *			iterator;
		typedef const T *	const_iterator;

		bool			IsBegin (const_iterator pIter)	const;						// первый?
		bool			IsEnd (const_iterator pIter)	const;						// последний?

		iterator		Begin ()					{ return ptr(); }				// возвращает первый элемент
		const_iterator	Begin () const				{ return ptr(); }

		iterator		End ()						{ return ptr() + _uCount; }		// возвращает указатель на конец массива
		const_iterator	End () const				{ return ptr() + _uCount; }

		iterator		GetIter (usize i);											// возвращает элемент
		const_iterator	GetIter (usize i) const;


	// static methods
	public:
		template <typename B>
		static Self From (B *ptr, usize count);

		static Self FromVoid (void_ptr_t ptr, usize size);

		template <typename B>
		static Self FromType (B &ref);
	};
	

	
	template <typename T>
	inline TUniArray<T>::TUniArray():
		_pElements(null), _uCount(0)
	{}


	template <typename T>
	inline TUniArray<T>::TUniArray (void_ptr_t pBegin, void_ptr_t pEnd):
		_pElements( (T*)pBegin ), _uCount( ( usize(pEnd) - usize(pBegin) ) / sizeof(T) )
	{
		ASSUME( _uCount == 0 or _pElements != null );
		ASSUME( pBegin <= pEnd );
	}


	template <typename T>
	inline TUniArray<T>::TUniArray(T *pElements, usize uCount): _pElements(pElements), _uCount(uCount)
	{
		ASSUME( _uCount == 0 or _pElements != null );
	}
	

	template <typename T>
	template <usize I>
	inline TUniArray<T>::TUniArray(const T (&arr)[I]): _pElements( (T*)arr ), _uCount(I)
	{
		ASSUME( _uCount == 0 or _pElements != null );
	}
	

	template <typename T>
	template <typename B>
	TUniArray<T> TUniArray<T>::From (B *ptr, usize count)
	{
		STATIC_ASSERTE( sizeof(B) % sizeof(T) == 0 );
		return FromVoid( (void_ptr_t) ptr, count * sizeof(B) );
	}
	

	template <typename T>
	TUniArray<T> TUniArray<T>::FromVoid (void_ptr_t ptr, usize size)
	{
		return TUniArray<T>( (T*) ptr, size / sizeof(T) );
	}

	
	template <typename T>
	template <typename B>
	TUniArray<T> TUniArray<T>::FromType (B &ref)
	{
		STATIC_ASSERTE( sizeof(B) % sizeof(T) == 0 );
		return FromVoid( (void_ptr_t) &ref, sizeof(B) );
	}


	template <typename T>
	inline T * TUniArray<T>::ptr()
	{
		ASSUME( _pElements != null );
		return _pElements;
	}
	

	template <typename T>
	inline T const * TUniArray<T>::ptr() const
	{
		ASSUME( _pElements != null );
		return _pElements;
	}
		

	template <typename T>
	inline T & TUniArray<T>::Back()
	{
		ASSUME( _uCount > 0 );
		return _pElements[ _uCount-1 ];
	}

	
	template <typename T>
	inline T const & TUniArray<T>::Back() const
	{
		ASSUME( _uCount > 0 );
		return _pElements[ _uCount-1 ];
	}

	
	template <typename T>
	inline T & TUniArray<T>::Front()
	{
		ASSUME( _uCount > 0 );
		return _pElements[0];
	}

	
	template <typename T>
	inline T const & TUniArray<T>::Front() const
	{
		ASSUME( _uCount > 0 );
		return _pElements[0];
	}

	
	template <typename T>
	inline T & TUniArray<T>::operator [] (usize i)
	{
		ASSUME( i < _uCount );
		return _pElements[i];
	}

	
	template <typename T>
	inline T const & TUniArray<T>::operator [] (usize i) const
	{
		ASSUME( i < _uCount );
		return _pElements[i];
	}
		
	
	template <typename T>
	inline bool TUniArray<T>::operator == (const Self &sArray) const
	{
		return Compare( sArray, _CompareElements() );
	}

	
	template <typename T>
	inline bool TUniArray<T>::operator != (const Self &sArray) const
	{
		return not ( *this == sArray );
	}
	

	template <typename T>
	template <typename Cmp>
	inline bool TUniArray<T>::Compare (const Self &sArray, Cmp sCmp) const
	{
		if ( _uCount != sArray._uCount )
			return false;

		if ( sArray._pElements == _pElements ) {
			WARNING("");
			return true;
		}

		for (usize i = 0; i < _uCount; ++i) {
			if ( not sCmp( _pElements[i], sArray._pElements[i] ) )
				return false;
		}

		return true;
	}


	template <typename T>
	inline usize TUniArray<T>::GetIndex (const T &elem) const
	{
		ASSERT( IsInArray( elem ) );
		return usize( &elem - Begin() );
	}
	
	
	template <typename T>
	inline bool TUniArray<T>::IsInArray (const T &elem) const
	{
		return ( &elem >= Begin() and &elem < End() );
	}
	
	
	template <typename T>
	template <typename E>
	inline bool TUniArray<T>::Find (OUT usize &uIndex, const E &tElem, usize uStart) const
	{
		uIndex = -1;

		FORv( i, uStart, *this )
		{
			if ( tElem == _pElements[i] )
			{
				uIndex = i;
				return true;
			}
		}
		return false;
	}

	
	template <typename T>
	template <typename E>
	inline bool TUniArray<T>::IsExist (const E &tElem) const
	{
		usize	idx;
		return Find( idx, tElem, 0 );
	}


	template <typename T>
	inline TUniArray<T>  TUniArray<T>::SubArray (usize first, usize count)
	{
		ASSERT( first < Count() and first + count <= Count() );
		
		if ( first >= Count() )
			return TUniArray<T>();

		if ( count + first >= Count() )
			count = Count() - first;

		return ( TUniArray<T>( ptr() + first, count ) );
	}

	
	template <typename T>
	inline TUniArray<const T>  TUniArray<T>::SubArray (usize first, usize count) const
	{
		ASSERT( first < Count() and first + count <= Count() );
		
		if ( first >= Count() )
			return TUniArray<const T>();

		if ( count + first >= Count() )
			count = Count() - first;

		return ( TUniArray<const T>( ptr() + first, count ) );
	}
	
	
	template <typename T>
	inline T * TUniArray<T>::GetIter (usize i)
	{
		ASSUME( i < _uCount );
		return & _pElements[i];
	}

	
	template <typename T>
	inline const T * TUniArray<T>::GetIter (usize i) const
	{
		ASSUME( i < _uCount );
		return & _pElements[i];
	}

	
	template <typename T>
	inline bool TUniArray<T>::IsBegin (const_iterator pIter) const
	{
		return Begin() == pIter;
	}

	
	template <typename T>
	inline bool TUniArray<T>::IsEnd (const_iterator pIter) const
	{
		return End() == pIter;
	}


#	undef TUniArray


}	// UXTypes
}	// UX_STL

#endif	// _UXS_UNI_ARRAY_TYPE_H